package com.ddxz.tool.core.convert;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ddxz.tool.core.exception.DdxzBaseException;
import net.sf.cglib.core.Converter;

import java.lang.reflect.Constructor;
import java.util.*;

import static com.ddxz.tool.core.convert.ConvertType.*;
import static com.ddxz.tool.core.exception.DdxzErrorStatus.CAN_NOT_COPY;
import static com.ddxz.tool.core.exception.DdxzErrorStatus.STR_DATE_FAILED;

/**
 * @author 朱宗刚
 * @version 1.0.0
 * @date 2020/2/28
 */
public class DdxzUtil implements Converter {
    @Override
    public Object convert(Object src, Class target, Object context) {
        if (null == src || src.getClass().equals(target)) {
            return src;
        }

        ConvertType type = typeCopyMapping.get(getSupportClass(src.getClass()) + "->" + getSupportClass(target));
        if (null == type) {
            throw new DdxzBaseException(CAN_NOT_COPY, src.getClass().getName(), target.getName());
        }

        switch (type) {
            case JSONARRAY_STR:
            case JSONOBJECT_STR:
                return ((JSON) src).toString();
            case STR_JSONARRAY:
                return JSONArray.parseArray((String) src);
            case STR_JSONOBJECT:
                return JSONObject.parseObject((String) src);
            case LONG_STR:
                return longToStr((Long) src);
            case STR_LONG:
                return strToLong((String) src);
            case INTEGER_STR:
                return integerToStr((Integer) src);
            case STR_INTEGER:
                return strToInteger((String) src);
            case COLLECT_COLLECT:
                return copyCollect((Collection) src, target);
            case COLLECT_STRING:
                return StringUtil.join((Collection) src);
            case STRING_COLLECT:
                return strToCollect((String) src, target);
            case DATE_STRING:
                return dateToString((Date) src);
            case STRING_DATE:
                return stringToDate((String) src);
            case FLOAT_STRING:
            case DOUBLE_STRING:
            case SHORT_STR:
                return src.toString();
            case STRING_FLOAT:
                return StringUtil.toFloat((String) src);
            case STRING_DOUBLE:
                return StringUtil.toDouble((String) src);
            case DATE_LONG:
                return dateToLong((Date) src);
            case LONG_DATE:
                return longToDate((Long) src);
            case SHORT_DOUBLE:
                return ((Short)src).doubleValue();
            case SHORT_FLOAT:
                return ((Short)src).floatValue();
            case SHORT_INTEGER:
                return ((Short)src).intValue();
            case SHORT_LONG:
                return ((Short) src).longValue();
            case STR_SHORT:
                return StringUtil.toShort((String) src);
            case FLOAT_LONG:
                return ((Float)src).longValue();
            case FLOAT_DOUBLE:
                return ((Float) src).doubleValue();
            case FLOAT_INTEGER:
                return ((Float)src).intValue();
            case FLOAT_SHORT:
                return ((Float)src).shortValue();
            case LONG_FLOAT:
                return ((Long)src).floatValue();
            case LONG_DOUBLE:
                return ((Long)src).doubleValue();
            case LONG_INTEGER:
                return ((Long)src).intValue();
            case LONG_SHORT:
                return ((Long)src).shortValue();
            case DOUBLE_FLOAT:
                return ((Double)src).floatValue();
            case DOUBLE_INTEGER:
                return ((Double)src).intValue();
            case DOUBLE_LONG:
                return ((Double)src).longValue();
            case DOUBLE_SHORT:
                return ((Double)src).shortValue();
            case INTEGER_LONG:
                return ((Integer)src).longValue();
            case INTEGER_FLOAT:
                return ((Integer)src).floatValue();
            case INTEGER_DOUBLE:
                return ((Integer)src).doubleValue();
            case INTEGER_SHORT:
                return ((Integer)src).shortValue();
            default:
                return src;
        }
    }

    private Date longToDate(Long src) {
        if(LONG_SEC_DATE == (type&DATE_CONVERT)) {
            return new Date(src * 1000L);
        } else {
            return new Date(src);
        }
    }

    private Long dateToLong(Date src) {
        if(LONG_MILLISEC_DATE == (type&DATE_CONVERT)) {
            return src.getTime();
        } else {
            return src.getTime() / 1000L;
        }
    }

    /**
     * 将字符串转换为容器
     *
     * @param src    待转换字符串
     * @param target 容器类型
     * @return 容器
     */
    private Collection strToCollect(String src, Class<?> target) {
        if (List.class.isAssignableFrom(target)) {
            return StringUtil.getList(src);
        }

        if (Set.class.isAssignableFrom(target)) {
            return new HashSet(StringUtil.getList(src));
        }

        throw new DdxzBaseException(CAN_NOT_COPY, src.getClass().getName(), target.getName());
    }

    private Date stringToDate(String src) {
        switch (type & CONVERT_DATE) {
            case JUST_DATE:
                return DateUtil.getDate(src);
            case STD_TIME:
                return DateUtil.getStdTime(src);
            case STD_DATETIME:
                return DateUtil.getStdDateTime(src);
            case CVT_TIME:
                return DateUtil.getCvtTime(src);
            case CVT_DATETIME:
                return DateUtil.getCvtDateTime(src);
            default:
                throw new DdxzBaseException(STR_DATE_FAILED, src);
        }
    }

    private Long strToLong(String src) {
        switch (type & CONVERT_DATE) {
            case JUST_DATE:
                return DateUtil.getDate(src).getTime();
            case STD_TIME:
                return DateUtil.getStdTime(src).getTime();
            case STD_DATETIME:
                return DateUtil.getStdDateTime(src).getTime();
            case CVT_TIME:
                return DateUtil.getCvtTime(src).getTime();
            case CVT_DATETIME:
                return DateUtil.getCvtDateTime(src).getTime();
            default:
                return StringUtil.toLong(src);
        }
    }

    private String dateToString(Date date) {
        switch (type & CONVERT_DATE) {
            case JUST_DATE:
                return DateUtil.toStrDate(date);
            case STD_TIME:
                return DateUtil.toStdTime(date);
            case CVT_TIME:
                return DateUtil.toCvtTime(date);
            case STD_DATETIME:
                return DateUtil.toStdDateTime(date);
            default:
                return DateUtil.toCvtDateTime(date);
        }
    }

    private Collection copyCollect(Collection src, Class<?> target) {
        try {
            Collection tmp;
            if (List.class.isAssignableFrom(target)) {
                tmp = new ArrayList(src.size());
            } else if (Set.class.isAssignableFrom(target)) {
                tmp = new HashSet(src.size());
            } else {
                Constructor constructor = target.getConstructor();
                tmp = (Collection) constructor.newInstance();
            }

            CollectionUtil.copyCollection(src, tmp, tarItemClass);
            return tmp;
        } catch (Exception e) {
            throw new DdxzBaseException(CAN_NOT_COPY, src.getClass().getName(), target.getName());
        }
    }

    private String longToStr(Long src) {
        switch (type & DATE_CONVERT) {
            case MILLISEC_DATE:
                return DateUtil.toCvtDate(src);
            case MILLISEC_TIME:
                return DateUtil.toCvtTime(src);
            case MILLISEC_DATETIME:
                return DateUtil.toCvtDateTime(src);
            case SECOND_DATE:
                return DateUtil.toStdDate(src);
            case SECOND_DATETIME:
                return DateUtil.toStdDateTime(src);
            case SECOND_TIME:
                return DateUtil.toStdTime(src);
            default:
                return src.toString();
        }
    }

    private String integerToStr(Integer src) {
        if ((type & VERSION_CONVERT) == VERSION_INT) {
            return VersionUtil.verToStr(src);
        } else {
            return src.toString();
        }
    }

    private Integer strToInteger(String src) {
        if ((type & VERSION_CONVERT) == VERSION_INT) {
            return VersionUtil.strToVer(src);
        } else {
            return StringUtil.toInteger(src);
        }
    }

    public DdxzUtil(int type, Class<?> tarItemClass) {
        this.type = type;
        this.tarItemClass = tarItemClass;
    }

    public DdxzUtil(int type) {
        this.type = type;
    }

    private String getSupportClass(Class<?> sub) {
        if (JSON.class.isAssignableFrom(sub)) {
            return sub.getName();
        }

        if (Collection.class.isAssignableFrom(sub)) {
            return Collection.class.getName();
        }

        if (sub.isArray()) {
            return "ARRAY";
        }

        return sub.getName();
    }

    private int type;

    private Class<?> tarItemClass;

    private static Map<String, ConvertType> typeCopyMapping = new HashMap<>();

    /**
     * 精确到秒的时间戳转换为字符串表示的日期
     */
    public static final int SECOND_DATE = 0x01;
    /**
     * 精确到秒的时间戳转换为字符串表示的时间
     */
    public static final int SECOND_TIME = 0x02;
    /**
     * 精确到秒的时间戳转换为字符串表示的日期时间
     */
    public static final int SECOND_DATETIME = 0x03;
    /**
     * 精确到毫秒的时间戳转换为字符串表示的日期
     */
    public static final int MILLISEC_DATE = 0x04;
    /**
     * 精确到毫秒的时间戳转换为字符串表示的时间
     */
    public static final int MILLISEC_TIME = 0x05;
    /**
     * 精确到毫秒的时间戳转换为字符串表示的日期时间
     */
    public static final int MILLISEC_DATETIME = 0x06;
    /**
     * 精确到毫秒的时间戳转换为Date类型
     */
    public static final int LONG_SEC_DATE = 0x07;
    /**
     * 精确到秒的时间戳转换为Date类型
     */
    public static final int LONG_MILLISEC_DATE = 0x08;

    /**
     * 日期类型与字符串类型互转时，精确到天
     */
    public static final int JUST_DATE = 0x100;
    /**
     * 时间类型与字符串类型互转时，精确到秒
     */
    public static final int STD_TIME = 0x200;
    /**
     * 时间类型与字符串类型互转时，精确到毫秒
     */
    public static final int CVT_TIME = 0x300;
    /**
     * 日期时间类型与字符串类型互转时，精确到秒
     */
    public static final int STD_DATETIME = 0x400;
    /**
     * 日期时间类型与字符串类型互转时，精确到毫秒
     */
    public static final int CVT_DATETIME = 0x500;

    /**
     * 版本号的文本表示法与整数表示法直接的转换
     */
    public static final int VERSION_INT = 0x10;

    /**
     * 将时间戳转换为字符串表示的日期时间
     */
    public static final int DATE_CONVERT = 0x0F;
    /**
     * 版本号在字符串类型与整数直接的转换
     */
    private static final int VERSION_CONVERT = 0xF0;
    /**
     * 日期时间类型与字符串类型的相互转换
     */
    private static final int CONVERT_DATE = 0xF00;
    /**
     * 不做特殊处理，安装最简单方式处理
     */
    public static final int NONE_CONVERT = 0x00;

    static {
        typeCopyMapping.put(JSONObject.class.getName() + "->" + String.class.getName(), JSONOBJECT_STR);
        typeCopyMapping.put(JSONArray.class.getName() + "->" + String.class.getName(), JSONARRAY_STR);
        typeCopyMapping.put(String.class.getName() + "->" + JSONObject.class.getName(), STR_JSONOBJECT);
        typeCopyMapping.put(String.class.getName() + "->" + JSONArray.class.getName(), STR_JSONARRAY);
        typeCopyMapping.put(Collection.class.getName() + "->" + String.class.getName(), COLLECT_STRING);
        typeCopyMapping.put(Collection.class.getName() + "->" + Collection.class.getName(), COLLECT_COLLECT);
        typeCopyMapping.put(Long.class.getName() + "->" + String.class.getName(), LONG_STR);
        typeCopyMapping.put(Integer.class.getName() + "->" + String.class.getName(), INTEGER_STR);
        typeCopyMapping.put(String.class.getName() + "->" + Long.class.getName(), STR_LONG);
        typeCopyMapping.put(String.class.getName() + "->" + Integer.class.getName(), STR_INTEGER);
        typeCopyMapping.put("ARRAY->ARRAY", ARRAY_ARRAY);
        typeCopyMapping.put("ARRAY->" + Collection.class.getName(), ARRAY_COLLECT);
        typeCopyMapping.put("ARRAY->" + String.class.getName(), ARRAY_STR);
        typeCopyMapping.put(Collection.class.getName() + "->ARRAY", COLLECT_ARRAY);
        typeCopyMapping.put(String.class.getName() + "->" + Collection.class.getName(), STRING_COLLECT);
        typeCopyMapping.put(String.class.getName() + "->" + Collection.class.getName(), STRING_COLLECT);
        typeCopyMapping.put(Date.class.getName() + "->" + String.class.getName(), DATE_STRING);
        typeCopyMapping.put(String.class.getName() + "->" + Date.class.getName(), STRING_DATE);
        typeCopyMapping.put(Double.class.getName() + "->" + String.class.getName(), DOUBLE_STRING);
        typeCopyMapping.put(String.class.getName() + "->" + Double.class.getName(), STRING_DOUBLE);
        typeCopyMapping.put(Float.class.getName() + "->" + String.class.getName(), FLOAT_STRING);
        typeCopyMapping.put(String.class.getName() + "->" + Float.class.getName(), STRING_FLOAT);
        typeCopyMapping.put(Float.class.getName() + "->" + Double.class.getName(), FLOAT_DOUBLE);
        typeCopyMapping.put(Float.class.getName() + "->" + Long.class.getName(), FLOAT_LONG);
        typeCopyMapping.put(Float.class.getName() + "->" + Integer.class.getName(), FLOAT_INTEGER);
        typeCopyMapping.put(Float.class.getName() + "->" + Short.class.getName(), FLOAT_SHORT);
        typeCopyMapping.put(Double.class.getName() + "->" + Float.class.getName(), DOUBLE_FLOAT);
        typeCopyMapping.put(Double.class.getName() + "->" + Long.class.getName(), DOUBLE_LONG);
        typeCopyMapping.put(Double.class.getName() + "->" + Integer.class.getName(), DOUBLE_INTEGER);
        typeCopyMapping.put(Double.class.getName() + "->" + Short.class.getName(), DOUBLE_SHORT);
        typeCopyMapping.put(Long.class.getName() + "->" + Float.class.getName(), LONG_FLOAT);
        typeCopyMapping.put(Long.class.getName() + "->" + Double.class.getName(), LONG_DOUBLE);
        typeCopyMapping.put(Long.class.getName() + "->" + Short.class.getName(), LONG_SHORT);
        typeCopyMapping.put(Long.class.getName() + "->" + Integer.class.getName(), LONG_INTEGER);
        typeCopyMapping.put(Integer.class.getName() + "->" + Float.class.getName(), INTEGER_FLOAT);
        typeCopyMapping.put(Integer.class.getName() + "->" + Double.class.getName(), INTEGER_DOUBLE);
        typeCopyMapping.put(Integer.class.getName() + "->" + Short.class.getName(), INTEGER_SHORT);
        typeCopyMapping.put(Integer.class.getName() + "->" + Long.class.getName(), INTEGER_LONG);
        typeCopyMapping.put(Short.class.getName() + "->" + Integer.class.getName(), SHORT_INTEGER);
        typeCopyMapping.put(Short.class.getName() + "->" + Long.class.getName(), SHORT_LONG);
        typeCopyMapping.put(Short.class.getName() + "->" + Float.class.getName(), SHORT_FLOAT);
        typeCopyMapping.put(Short.class.getName() + "->" + Double.class.getName(), SHORT_DOUBLE);
        typeCopyMapping.put(Short.class.getName() + "->" + String.class.getName(), SHORT_STR);
        typeCopyMapping.put(String.class.getName() + "->" + Short.class.getName(), STR_SHORT);
        typeCopyMapping.put(Long.class.getName() + "->" + Date.class.getName(), LONG_DATE);
        typeCopyMapping.put(Date.class.getName() + "->" + Long.class.getName(), DATE_LONG);
    }
}
